package sheep.dubbo.generic.controller;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.dubbo.config.ReferenceConfig;
import org.apache.dubbo.config.utils.ReferenceConfigCache;
import org.apache.dubbo.rpc.service.GenericService;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import sheep.dubbo.generic.dto.MetaData;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Slf4j
@RestController
public class ConsumerGenericInvokeController {

    @PostMapping(value = "/generic/invoke/single")
    public Object genericInvokeSingle(@RequestBody MetaData dto) {
        log.info("调用接口开始...{}", JSON.toJSONString(dto));
        // 创建服务实例
        ReferenceConfig<GenericService> reference = new ReferenceConfig<>();
        reference.setGeneric("true");
        reference.setInterface(dto.getInterfaceName());
        reference.setVersion(dto.getVersion());

        // 获取缓存中的实例
        // 这里不能直接使用reference.get()，否则容易造成内存泄漏，或者zk宕机
        ReferenceConfigCache cache = ReferenceConfigCache.getCache();
        GenericService genericService = cache.get(reference);

        Map<String, Object> paramNameMap = JSON.parseObject(dto.getArgs(), new TypeReference<HashMap<String, Object>>() {
        });
        log.info("paramNameMap：{}", JSON.toJSONString(paramNameMap));
        // 同步调用实例
        Object result = genericService.$invoke(dto.getMethod(), new String[]{dto.getParameterTypes()}, new Object[]{paramNameMap});
        log.info(">>>>>调用dubbo服务接口,入参:{},出参:{}", dto, result);
        return result;
    }

    @PostMapping(value = "/generic/invoke/single/generic")
    public Object genericInvokeSingleIncludGeneric(@RequestBody MetaData dto) {
        log.info("调用接口开始...{}", JSON.toJSONString(dto));
        // 创建服务实例
        ReferenceConfig<GenericService> reference = new ReferenceConfig<>();
        reference.setGeneric("true");
        reference.setInterface(dto.getInterfaceName());
        reference.setVersion(dto.getVersion());

        // 获取缓存中的实例
        // 这里不能直接使用reference.get()，否则容易造成内存泄漏，或者zk宕机
        ReferenceConfigCache cache = ReferenceConfigCache.getCache();
        GenericService genericService = cache.get(reference);

        final String[] parameterType = dto.getParameterTypes().replaceAll(">","").split("<");

        StringBuilder body = new StringBuilder(dto.getArgs());
        for (int i = 0;i< parameterType.length;i++ ){
            // 替换并拼接 class
            body.insert(StringUtils.lastOrdinalIndexOf(body, "}", i+1),",\"class\":\""+parameterType[i]+"\"");
        }

        Map<String, Object> paramNameMap = JSON.parseObject(body.toString(), new TypeReference<HashMap<String, Object>>() {
        });
        log.info("paramNameMap：{}", JSON.toJSONString(paramNameMap));
        // 同步调用实例
        Object result = genericService.$invoke(dto.getMethod(), new String[]{parameterType[0]}, new Object[]{paramNameMap});
        log.info(">>>>>调用dubbo服务接口,入参:{},出参:{}", dto, result);
        return result;
    }

    @PostMapping(value = "/generic/invoke/single/list")
    public Object genericInvokeSingleList(@RequestBody MetaData dto) {
        log.info("调用接口开始...{}", JSON.toJSONString(dto));
        // 创建服务实例
        ReferenceConfig<GenericService> reference = new ReferenceConfig<>();
        reference.setGeneric("true");
        reference.setInterface(dto.getInterfaceName());
        reference.setVersion(dto.getVersion());

        // 获取缓存中的实例
        // 这里不能直接使用reference.get()，否则容易造成内存泄漏，或者zk宕机
        ReferenceConfigCache cache = ReferenceConfigCache.getCache();
        GenericService genericService = cache.get(reference);

        final List<Object> objects = JSON.parseArray(dto.getArgs(), Object.class);
        log.info("paramNameMap：{}", JSON.toJSONString(objects));
        // 同步调用实例
        Object result = genericService.$invoke(dto.getMethod(), new String[]{dto.getParameterTypes()}, new Object[]{objects});
        log.info(">>>>>调用dubbo服务接口,入参:{},出参:{}", dto, result);
        return result;
    }

}